package edu.northwestern.cbits.purple_robot_manager.plugins; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.FilenameFilter; import java.io.IOException; import java.io.OutputStreamWriter; import java.security.SecureRandom; import java.util.List; import java.util.Map; import org.apache.commons.io.FileUtils; import android.annotation.SuppressLint; import android.app.ActivityManager.RunningTaskInfo; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.ApplicationInfo; import android.location.Location; import android.net.wifi.ScanResult; import android.os.Bundle; import android.preference.PreferenceManager; import android.util.JsonWriter; import android.util.Log; import edu.northwestern.cbits.purple_robot_manager.R; import edu.northwestern.cbits.purple_robot_manager.logging.LogManager; import edu.northwestern.cbits.purple_robot_manager.probes.Probe; // NOTE: This plugin is included for the purposes of comparison between // Android's streaming JSON classes and Jackson. The Jackson class is the one // under continual development and should be used instead of this one. @SuppressLint("NewApi") public class StreamingJSONUploadPlugin extends DataUploadPlugin { private final static String FILE_EXTENSION = ".streaming"; private static final String ENABLED = "config_enable_streaming_json_data_server"; private JsonWriter _writer = null; private long _lastAttempt = 0; private File _currentFile = null; public String[] respondsTo() { String[] activeActions = { Probe.PROBE_READING, OutputPlugin.FORCE_UPLOAD }; return activeActions; } private void uploadFiles(final Context context, final SharedPreferences prefs) { long now = System.currentTimeMillis(); if (now - this._lastAttempt < 300000) return; this._lastAttempt = now; final StreamingJSONUploadPlugin me = this; Runnable r = new Runnable() { public void run() { try { File pendingFolder = me.getPendingFolder(); String[] filenames = pendingFolder.list(new FilenameFilter() { public boolean accept(File dir, String filename) { return filename.endsWith(StreamingJSONUploadPlugin.FILE_EXTENSION); } }); if (filenames == null) filenames = new String[0]; if (filenames.length < 2) return; SecureRandom random = new SecureRandom(); int index = random.nextInt(filenames.length - 1); File payloadFile = new File(pendingFolder, filenames[index]); String payload = FileUtils.readFileToString(payloadFile); if (me.transmitPayload(prefs, payload) == DataUploadPlugin.RESULT_SUCCESS) { payloadFile.delete(); me._lastAttempt = 0; me.uploadFiles(context, prefs); } } catch (IOException e) { LogManager.getInstance(context).logException(e); me.broadcastMessage(context.getString(R.string.message_general_error), true); } } }; Thread t = new Thread(r); t.start(); } public void processIntent(final Intent intent) { final Context context = this.getContext().getApplicationContext(); final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); if (prefs.getBoolean(StreamingJSONUploadPlugin.ENABLED, false) == false) return; String action = intent.getAction(); if (OutputPlugin.FORCE_UPLOAD.equals(action)) { this._lastAttempt = 0; this.uploadFiles(context, prefs); } else if (Probe.PROBE_READING.equals(action)) { Bundle extras = intent.getExtras(); if (extras.containsKey(DataUploadPlugin.TRANSMIT_KEY) && extras.getBoolean(DataUploadPlugin.TRANSMIT_KEY) == false) return; long now = System.currentTimeMillis(); try { if (this._currentFile != null) { File f = this._currentFile.getAbsoluteFile(); long length = f.length(); long modDelta = now - f.lastModified(); // TODO: Make configurable... if (this._writer != null && (length > 262144 || modDelta > 60000)) { this._writer.endArray(); this._writer.flush(); this._writer.close(); this._writer = null; this._currentFile = null; } } this.uploadFiles(context, prefs); if (this._writer == null) { this._currentFile = new File(this.getPendingFolder(), now + FILE_EXTENSION); BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(this._currentFile)); this._writer = new JsonWriter(new OutputStreamWriter(out, "UTF-8")); this._writer.beginArray(); } StreamingJSONUploadPlugin.writeBundle(this.getContext(), this._writer, extras); this._writer.flush(); } catch (IOException e) { LogManager.getInstance(this.getContext()).logException(e); } } } @SuppressWarnings("unchecked") public static void writeBundle(Context context, JsonWriter writer, Bundle bundle) { try { writer.beginObject(); Map<String, Object> values = OutputPlugin.getValues(bundle); for (String key : values.keySet()) { Object value = values.get(key); if (value == null || key == null) { // Skip } else { writer.name(key); if (value instanceof String) { writer.value((String) value); } else if (value instanceof float[]) { float[] floats = (float[]) value; writer.beginArray(); for (float f : floats) writer.value(f); writer.endArray(); } else if (value instanceof int[]) { int[] ints = (int[]) value; writer.beginArray(); for (int i : ints) writer.value(i); writer.endArray(); } else if (value instanceof long[]) { long[] longs = (long[]) value; writer.beginArray(); for (long l : longs) writer.value(l); writer.endArray(); } else if (value instanceof double[]) { double[] doubles = (double[]) value; writer.beginArray(); for (double d : doubles) writer.value(d); writer.endArray(); } else if (value instanceof Float) { Float f = (Float) value; writer.value(f); } else if (value instanceof Integer) { Integer i = (Integer) value; writer.value(i); } else if (value instanceof Long) { Long l = (Long) value; writer.value(l); } else if (value instanceof Boolean) { Boolean b = (Boolean) value; writer.value(b); } else if (value instanceof Short) { Short s = (Short) value; writer.value(s); } else if (value instanceof Double) { Double d = (Double) value; if (d.isInfinite()) writer.value(Double.MAX_VALUE); else writer.value(d); } else if (value instanceof List) { List<Object> list = (List<Object>) value; writer.beginArray(); for (Object o : list) { if (o instanceof String) writer.value(o.toString()); else if (o instanceof Bundle) StreamingJSONUploadPlugin.writeBundle(context, writer, (Bundle) o); else if (o instanceof ScanResult) { ScanResult s = (ScanResult) o; writer.beginObject(); if (s.BSSID != null) writer.name("BSSID").value(s.BSSID); if (s.SSID != null) writer.name("SSID").value(s.SSID); if (s.capabilities != null) writer.name("Capabilities").value(s.capabilities); writer.name("Frequency").value(s.frequency); writer.name("Level dBm").value(s.level); writer.endObject(); } else if (o instanceof RunningTaskInfo) { RunningTaskInfo r = (RunningTaskInfo) o; writer.beginObject(); if (r.baseActivity != null) writer.name("Base Activity").value(r.baseActivity.getPackageName()); if (r.description != null) writer.name("Description").value(r.description.toString()); writer.name("Activity Count").value(r.numActivities); writer.name("Running Activity Count").value(r.numRunning); writer.endObject(); } else if (o instanceof ApplicationInfo) { ApplicationInfo a = (ApplicationInfo) o; writer.value(a.packageName); } else if (o instanceof Location) { Location l = (Location) o; writer.beginObject(); writer.name("Accuracy").value(l.getAccuracy()); writer.name("Altitude").value(l.getAltitude()); writer.name("Bearing").value(l.getBearing()); writer.name("Latitude").value(l.getLatitude()); writer.name("Longitude").value(l.getLongitude()); if (l.getProvider() != null) writer.name("Provider").value(l.getProvider()); else writer.name("Provider").value("Unknown"); writer.name("Speed").value(l.getSpeed()); writer.name("Timestamp").value(l.getTime()); writer.endObject(); } else Log.e("PR", "LIST OBJ: " + o.getClass().getCanonicalName() + " IN " + key); } writer.endArray(); } else if (value instanceof Location) { Location l = (Location) value; writer.beginObject(); writer.name("Accuracy").value(l.getAccuracy()); writer.name("Altitude").value(l.getAltitude()); writer.name("Bearing").value(l.getBearing()); writer.name("Latitude").value(l.getLatitude()); writer.name("Longitude").value(l.getLongitude()); if (l.getProvider() != null) writer.name("Provider").value(l.getProvider()); else writer.name("Provider").value("Unknown"); writer.name("Speed").value(l.getSpeed()); writer.name("Timestamp").value(l.getTime()); writer.endObject(); } else if (value instanceof BluetoothClass) { BluetoothClass btClass = (BluetoothClass) value; writer.value(btClass.toString()); } else if (value instanceof BluetoothDevice) { BluetoothDevice device = (BluetoothDevice) value; writer.beginObject(); if (device.getBondState() == BluetoothDevice.BOND_BONDED) writer.name("Bond State").value("Bonded"); else if (device.getBondState() == BluetoothDevice.BOND_BONDING) writer.name("Bond State").value("Bonding"); else writer.name("Bond State").value("None"); writer.name("Device Address").value(device.getAddress()); writer.name("Device Class").value(device.getBluetoothClass().toString()); writer.endObject(); } else if (value instanceof Bundle) StreamingJSONUploadPlugin.writeBundle(context, writer, (Bundle) value); else Log.e("PR", "GOT TYPE " + value.getClass().getCanonicalName() + " FOR " + key); } } writer.endObject(); } catch (IOException e) { LogManager.getInstance(context).logException(e); } } public boolean isEnabled(Context context) { return false; } }